import web3Utils from 'web3-utils'
import crypto from "crypto";
import api from "@/request/api";
import { BigNumber } from 'bignumber.js'
import { global } from "@/config/config";
import { getValue } from './deviceStorage';

const defaultTokenTransferAsset = {
    type: 'Transfer',
    name: global.NOT_AVAILABLE,
    logoUri: global.NOT_AVAILABLE,
    directionSign: '',
    amountWithSymbol: global.NOT_AVAILABLE,
    tokenType: 'UNKNOWN',
}

export const guid = () => {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0,
            v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

export const max = (a, b) => {
    if (a >= b) {
        return a
    }
    if (a < b) {
        return b
    }
}

export const checkAddress = async (payload) => {
    //1.检查地址的有效性，调用web3.utils.toChecksumAddress
    let result = web3Utils.checkAddressChecksum(payload.address)
    if (result) {
        //2.调用接口查询地址的类型，合约地址或普通地址
        api.check_address(payload.address).then(res => {
            if (res.result !== '0x' && payload.type === 'contract') {
                return true
            } else if (res.result === '0x' && payload.type === 'ordinary') {
                return true
            } else {
                return false
            }
        }).catch(() => {
            return false
        })
    } else {
        return false
    }
}

export const getLabel = (value) => {
    if (value === 'USDT') {
        return 'USDT-ERC20'
    } else {
        return value
    }
}

export const getFullName = (value) => {
    if (value) {
        value = value.toUpperCase()
    }
    let result
    switch (value) {
        case 'ETH':
            result = 'Ethereum'
            break;
        case 'BTC':
            result = 'Bitcoin'
            break;
        case 'LTC':
            result = 'Litecoin'
            break;
        case 'BCS':
            result = 'Binance Smart Chain'
            break;
        case 'EOS':
            result = 'Enterprise Operation System'
            break;
        default:
            result = 'Ethereum'
            break;
    }
    return result
}

const isTransactionSummary = (value) => {
    return value.type === 'TRANSACTION'
}

const getLocalStartOfDate = (timestamp) => {
    const date = new Date(timestamp)
    return date.setHours(0, 0, 0, 0)
}

const sameString = (str1, str2) => {
    if (!str1 || !str2) {
        return false
    }
    return str1.toLowerCase() === str2.toLowerCase()
}

const sortObject = (obj, order = 'asc') => {
    return Object.keys(obj)
        .sort((a, b) => Number(order === 'desc' ? b : a) - Number(order === 'desc' ? a : b))
        .reduce(
            (acc, key) => ({
                ...acc,
                [key]: obj[key],
            }),
            {},
        )
}


export const formatHistoryData = (values = []) => {
    const newHistory = {}
    values.forEach((value) => {
        if (!isTransactionSummary(value)) {
            // DATE_LABEL is discarded as it's not needed for the current implementation
            return
        }

        const transaction = value.transaction
        const startOfDate = getLocalStartOfDate(transaction.timestamp)

        if (newHistory[startOfDate] === undefined) {
            newHistory[startOfDate] = []
        }

        const txIndex = newHistory[startOfDate].findIndex(({ id }) => sameString(id, transaction.id))

        if (txIndex >= 0) {
            newHistory[startOfDate][txIndex] = transaction
        } else {
            newHistory[startOfDate].push(transaction)
            // pushing a newer transaction to the existing list messes the transactions order
            // this happens when most recent transactions are added to the existing txs in the store
            newHistory[startOfDate] = newHistory[startOfDate].sort((a, b) => b.timestamp - a.timestamp)
        }
    })
    const historyTxs = Object.entries(sortObject(newHistory, 'desc'))
    return historyTxs
}


export const getTransactionType = (tx) => {
    let result
    switch (tx.txInfo.type) {
        case 'Creation': {
            result = { icon: 'Created', text: 'Safe created', size: 12 }
            break
        }
        case 'Transfer': {
            const isSendTx = tx.txInfo.direction === 'OUTGOING'
            result = {
                icon: isSendTx ? 'Send' : 'Received',
                text: isSendTx ? 'Send' : 'Received',
                size: 17
            }
            break
        }
        case 'SettingsChange': {
            // deleteGuard doesn't exist in Solidity
            // It is decoded as 'setGuard' with a settingsInfo.type of 'DELETE_GUARD'
            const isDeleteGuard = tx.txInfo.settingsInfo?.type === 'DELETE_GUARD'
            result = { icon: 'Created', text: isDeleteGuard ? 'deleteGuard' : tx.txInfo.dataDecoded.method, size: 12 }
            break
        }
        case 'Custom': {
            if (!tx.executionInfo) {
                result = { icon: 'Created', text: 'Module', size: 12 }
                break
            }
            if (tx.txInfo.isCancellation) {
                result = { icon: 'Rejection', text: 'On-chain rejection', size: 12 }
                break
            }
            if (tx.safeAppInfo) {
                result = { icon: tx.safeAppInfo.logoUri, text: tx.safeAppInfo.name, size: 12 }
                break
            }
            result = { icon: 'None', text: 'Contract interaction', size: 12 }
            break
        }
    }
    return result
}

const getAmountWithSymbol = (
    { decimals = 0, symbol = global.NOT_AVAILABLE, value }
) => {
    const finalValue = new BigNumber(value).times(`1e-${decimals}`).toFixed()
    const txAmount = finalValue === 'NaN' ? global.NOT_AVAILABLE : finalValue
    return `${txAmount} ${symbol.toUpperCase()}`
}

const getTxAmount = (txInfo, nativeCurrency) => {
    if (!txInfo || txInfo.type !== 'Transfer') {
        return global.NOT_AVAILABLE
    }
    switch (txInfo.transferInfo.type) {
        case global.TransactionTokenType.ERC20:
            return getAmountWithSymbol(
                {
                    decimals: `${txInfo.transferInfo.decimals ?? 0}`,
                    symbol: `${txInfo.transferInfo.tokenSymbol ?? global.NOT_AVAILABLE}`,
                    value: txInfo.transferInfo.value,
                }
            )
        case global.TransactionTokenType.ERC721:
            // simple workaround to avoid displaying unexpected values for incoming NFT transfer
            return `1 ${txInfo.transferInfo.tokenSymbol} ${txInfo.transferInfo.tokenId ? `(#${txInfo.transferInfo.tokenId})` : ''}`
        case global.TransactionTokenType.NATIVE_COIN: {
            return getAmountWithSymbol(
                {
                    decimals: nativeCurrency?.decimals,
                    symbol: nativeCurrency?.symbol,
                    value: txInfo.transferInfo.value,
                }
            )
        }
        default:
            return global.NOT_AVAILABLE
    }
}

const getTokenIdLabel = ({ tokenId }) => {
    return tokenId ? `(#${tokenId})` : ''
}

export const getTransactionAmount = (txInfo) => {
    let result = {}
    const nativeCurrency = getValue('chainDetail')?.nativeCurrency
    const amountWithSymbol = getTxAmount(txInfo, nativeCurrency)
    if (txInfo.type === 'Transfer') {
        const { direction, transferInfo } = txInfo
        const directionSign = direction === 'INCOMING' ? '+' : '-'
        switch (transferInfo.type) {
            case global.TransactionTokenType.ERC20: {
                result = {
                    type: 'Transfer',
                    name: transferInfo.tokenName ?? defaultTokenTransferAsset.name,
                    logoUri: transferInfo.logoUri ?? defaultTokenTransferAsset.logoUri,
                    directionSign,
                    amountWithSymbol,
                    tokenType: transferInfo.type,
                }
                break
            }
            case global.TransactionTokenType.ERC721: {
                result = {
                    type: 'Transfer',
                    name: `${transferInfo.tokenName ?? defaultTokenTransferAsset.name} ${getTokenIdLabel(transferInfo)}`,
                    logoUri: transferInfo.logoUri ?? defaultTokenTransferAsset.logoUri,
                    directionSign,
                    amountWithSymbol,
                    tokenType: transferInfo.type,
                }
                break
            }
            case global.TransactionTokenType.NATIVE_COIN: {
                result = {
                    type: 'Transfer',
                    name: nativeCurrency?.name ?? defaultTokenTransferAsset.name,
                    logoUri: nativeCurrency?.logoUri ?? defaultTokenTransferAsset.logoUri,
                    directionSign,
                    amountWithSymbol,
                    tokenType: transferInfo.type,
                }
                break
            }
        }
    }
    return result
}

export const AESencrypt = (key, iv, data) => {
    const decipher = crypto.createCipheriv('aes-128-cbc', key, iv);
    return decipher.update(data, 'binary', 'base64') + decipher.final('base64');
}

export const AESdecrypt = (key, iv, crypted) => {
    crypted = Buffer.from(crypted, 'base64').toString('binary')
    const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
    return decipher.update(crypted, 'binary', 'utf8') + decipher.final('utf8');
}